package svc.elib.socnet;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;

import org.apache.commons.collections15.Transformer;

import svc.elib.db.Author;
import svc.elib.db.Database;
import edu.uci.ics.jung.algorithms.scoring.BetweennessCentrality;
import edu.uci.ics.jung.algorithms.scoring.DistanceCentralityScorer;
import edu.uci.ics.jung.algorithms.scoring.EigenvectorCentrality;
import edu.uci.ics.jung.algorithms.scoring.PageRank;

/**
 * Calculates centrality metrics (betweennees, closeness and eigenvector centrality
 * for authors; betweennees for links)
 * 
 * @author svc
 * @date 8.6.2012
 */
public class CentralityMetrics {

	private Net net;
	
	/**
	 * transformes link to weight
	 */
	private class WeightTransformer implements Transformer<Link, Number> {

		@Override
		public Number transform(Link arg0) {
			return arg0.getWeight();
		}
	}
	
	private HashMap<String, Double> bScore = 
		new HashMap<String, Double>();
	
	private HashMap<String, Double> bLinkScore = 
		new HashMap<String, Double>();
	
	private HashMap<String, Double> prScore = 
		new HashMap<String, Double>();
	
	private HashMap<String, Double> evScore = 
		new HashMap<String, Double>();
	
	private HashMap<String, Double> cScore = 
		new HashMap<String, Double>();
	
	public CentralityMetrics(Net net) {
		this.net = net;
	}
	
	public void computePageRank(boolean weighted) {
		ConnectedComponents cc = new ConnectedComponents(net);
		cc.resolveComponents();
		
		for (int i = 0; i < cc.getNumComponents(); i++) {
			Net n = cc.getComponents().get(i);
			PageRank<Author, Link> ranker = null;
			if (weighted)
				ranker = new PageRank<Author, Link>(n.getGraph(), new WeightTransformer(), 0.05);
			else
				ranker = new PageRank<Author, Link>(n.getGraph(), 0.05);
			
			Collection<Author> nodeCol = n.getGraph().getVertices();
			Iterator<Author> nodeIt = nodeCol.iterator();
			while (nodeIt.hasNext()) {
				Author a = nodeIt.next();
				if (ranker.getVertexScore(a).isNaN())
					prScore.put(a.getName(), 0.0);
				else
					prScore.put(a.getName(), ranker.getVertexScore(a));
			}
		}
	}
	
	public void computeBetweeness(boolean weighted) {
		BetweennessCentrality<Author, Link> ranker = null;
		if (weighted) {
			ranker = new BetweennessCentrality<Author, Link>(net.getGraph(), new WeightTransformer());
		} else {
			ranker = new BetweennessCentrality<Author, Link>(net.getGraph());
		}
		
		Collection<Author> nodeCol = net.getGraph().getVertices();
		Iterator<Author> nodeIt = nodeCol.iterator();
		while (nodeIt.hasNext()) {
			Author a = nodeIt.next();
			if (ranker.getVertexScore(a).isNaN())
				bScore.put(a.getName(), 0.0);
			else
				bScore.put(a.getName(), ranker.getVertexScore(a));
		}
		
		Collection<Link> linkCol = net.getGraph().getEdges();
		Iterator<Link> linkIt = linkCol.iterator();
		while (linkIt.hasNext()) {
			Link l = linkIt.next();
			bLinkScore.put(l.getName(), ranker.getEdgeScore(l));
		}
	}
	
	public void computeEigenvector(boolean weighted) {
		ConnectedComponents cc = new ConnectedComponents(net);
		cc.resolveComponents();
		
		for (int i = 0; i < cc.getNumComponents(); i++) {
			Net n = cc.getComponents().get(i);
			EigenvectorCentrality<Author, Link> ranker = null;
			if (weighted) {
				ranker = new EigenvectorCentrality<Author, Link>(n.getGraph(), new WeightTransformer());
			} else {
				ranker = new EigenvectorCentrality<Author, Link>(n.getGraph());
			}
			
			Collection<Author> nodeCol = n.getGraph().getVertices();
			Iterator<Author> nodeIt = nodeCol.iterator();
			while (nodeIt.hasNext()) {
				Author a = nodeIt.next();
				if (ranker.getVertexScore(a).isNaN())
					evScore.put(a.getName(), 0.0);
				else
					evScore.put(a.getName(), ranker.getVertexScore(a));
			}
		}
	}
	
	public void computeCloseness(boolean weighted) {
		DistanceCentralityScorer<Author, Link> ranker = null;
		if (weighted) 
		{
			ranker = new DistanceCentralityScorer<Author, Link>(net.getGraph(), 
						new WeightTransformer(), true, true, true);
		} else {
			ranker = new DistanceCentralityScorer<Author, Link>(net.getGraph(), true, true, true);
		}
		
		Collection<Author> nodeCol = net.getGraph().getVertices();
		Iterator<Author> nodeIt = nodeCol.iterator();
		while (nodeIt.hasNext()) {
			Author a = nodeIt.next();
			if (ranker.getVertexScore(a).isNaN())
				cScore.put(a.getName(), 0.0);
			else
				cScore.put(a.getName(), ranker.getVertexScore(a));
		}
	}
	
	public double getBetweenness(String name) {
		Double s = bScore.get(name);
		if (s == null) {
			throw new IllegalArgumentException("Cardinal error, looking betweenness for " + name + ", but author does not exist");
		}
		
		return s.doubleValue();
	}
	
	public double getLinkBetweenness(String name) {
		Double s = bLinkScore.get(name);
		if (s == null) {
			throw new IllegalArgumentException("Cardinal error, looking betweenness for " + name + ", but author does not exist");
		}
		
		return s.doubleValue();
	}
	
	public double getCloseness(String name) {
		Double s = cScore.get(name);
		if (s == null) {
			throw new IllegalArgumentException("Cardinal error, looking closeness for " + name + ", but author does not exist");
		}
		
		return s.doubleValue();
	}
	
	public double getEigenvector(String name) {
		Double s = evScore.get(name);
		if (s == null) {
			throw new IllegalArgumentException("Cardinal error, looking eigenvector for " + name + ", but author does not exist");
		}
		
		return s.doubleValue();
	}
	
	public double getPageRank(String name) {
		Double s = prScore.get(name);
		if (s == null) {
			throw new IllegalArgumentException("Cardinal error, looking pagerank for " + name + ", but author does not exist");
		}
		
		return s.doubleValue();
	}
	
	
	public static void main(String[] args) 
		throws IOException
	{
		Database db = new Database("eLibData.csv", 1932, 2011);
		System.out.println(db.info());
		SocConstructor soc = new SocConstructor(db);
		Net net = soc.getNet();
		net.printInfo();
		
		PrintWriter pwNodes = new PrintWriter(new BufferedWriter(new FileWriter("tmp/centralityMetrics.csv")));
		
		CentralityMetrics cm = new CentralityMetrics(net);
		cm.computeBetweeness(false);
		cm.computeCloseness(false);
		cm.computeEigenvector(false);
		cm.computePageRank(false);
	
		Iterator<Author> ita = net.getGraph().getVertices().iterator();
		pwNodes.println("Name, BET, CLO, EIG, PR");
		while (ita.hasNext()) {
			Author a = ita.next();
			pwNodes.println(a.getName() + ", " + cm.getBetweenness(a.getName()) + ", " + cm.getCloseness(a.getName()) + ", " + cm.getEigenvector(a.getName()) + ", " + cm.getPageRank(a.getName()));
		}
		
		pwNodes.close();
	}
}
